#include "query.h"

void Query::setQueryString(std::string& query){
	queryString = query;
	vQuery = parseQuery( queryString);
}


std::vector<std::string> Query::parseQuery( std::string &query) {
	std::vector<std::string> v;
	std::string buf; // Have a buffer string
	std::stringstream ss(query); // Insert the string into a stream

	while (ss >> buf) {
		buf = trim(buf);
		v.push_back(buf);
	}
	return v;
}

void Query::executeQuery(std::string& query) {
	setQueryString(query);
	if (vQuery[0] == "create") {
		executeCreateQuery();
	}
	if (vQuery[0] == "select") {
		executeSelectQuery();
	}
	if (vQuery[0] == "drop") {
		executeDropQuery();
	}
	if (vQuery[0] == "insert") {
		executeInsertQuery();
	}
	if (vQuery[0] == "delete") {
		executeDeleteQuery();
	}
	if (vQuery[0] == "load") {
		load();
	}
	if (vQuery[0] == "unload") {
		unload();
	}
}

/** CREATE TABLE games
 (yr   INT NOT NULL PRIMARY KEY
 ,city VARCHAR(20)
 );
 * */
void Query::executeCreateQuery() {
	vQuery[2] = trim(vQuery[2]);
	Table *table = new Table(vQuery[2]);

	std::string paramString = queryString.substr(
			queryString.find_first_of('(') + 1,
			queryString.find_last_of(')') - queryString.find_first_of('(') - 1);

	std::vector < std::string > vParam = split(paramString, ',');

	std::string query = "";
	std::string colName = "";
	std::string dataType = "";
	int dataLength = 0;
	std::string tmpDataType = "";
	int hasNotNull = 0;
	int hasPK = 0;
	bool isNull = false;
	bool isPK = false;

	for (int i = 0; i < (int) vParam.size(); i++) {

		query = "";
		colName = "";
		dataType = "";
		dataLength = 0;
		tmpDataType = "";
		hasNotNull = 0;
		hasPK = 0;
		isNull = false;
		isPK = false;

//		std::cout << "\n param here" << vParam[i] << std::endl << std::endl;
		query = vParam[i];
		std::vector < std::string > vp;
		vp = parseQuery( query);
		colName = vp[0];
		dataType = vp[1];
		if (dataType.find("(") > 0) {
			tmpDataType = subStrByDelim(dataType, '(', ')');
			std::istringstream stream(tmpDataType);
			stream >> dataLength;
			dataType = dataType.substr(0, dataType.find('('));
		}
		isNull = ((int) query.find("not null")) > 0 ? true : false;
		isPK = ((int) query.find("primary key")) > 0 ? true : false;
		Column *col = new Column(colName, dataType, dataLength, isNull, isPK);
//				col->printObject();
		table->addColumn(col);
		//		table->printCols();
	}

	tables.push_back(table);
	std::cout << "\n Created table."  << std::endl;
}

//INSERT INTO games(yr,city) VALUES (2008,'Paris');
void Query::executeInsertQuery() {

	// games(yr,city) part
	std::string tableName = vQuery[2].substr(0, vQuery[2].find('('));
	tableName = trim(tableName);
	Table *table = getTableByName(tableName);

	// Check if table exist
	if (!table) {
		std::cout << (" \n Table does not exist !");
		return;
	}

	// Get columns name
	std::string sName =
			queryString.substr(
					queryString.find_first_of('(') + 1,
					queryString.find_first_of(')') - queryString.find_first_of(
							'(') - 1);
	std::vector < std::string > colNames = split(sName, ',');

	//  check if NOT NULL column is entered.
	std::list<Column*> cols = table->getAllCols();
	bool colexist = false;
	for (std::list<Column*>::iterator it = cols.begin(); it != cols.end(); it++) {
		colexist = false;
		Column *col = *it;
		for (int i = 0; i < (int) colNames.size(); i++) {
			colNames[i] = trim(colNames[i]);
			if (colNames[i] == col->getName()) {
				colexist = true;
			}
		}
		if (colexist == false && col->getIsNull() == true) {
			std::cout << "\n Column " << col->getName() << "must not be null !"
					<< std::endl;
			return;
		}
	}

	// check if all colums entered  exist
	for (int i = 0; i < (int) colNames.size(); i++) {
		colexist = false;
		for (std::list<Column*>::iterator it = cols.begin(); it != cols.end(); it++) {
			Column *col = *it;
			colNames[i] = trim(colNames[i]);
			if (colNames[i] == col->getName()) {
				colexist = true;
			}
		}
		if (colexist == false) {
			std::cout << "\n Column " << colNames[i] << " does not exist !"
					<< std::endl;
			return;
		}
	}

	int posValue = queryString.find("values");
	//	std::cout << "\n queryString.length() - (posValue + 7) : " << queryString.length() - (posValue + 7);
	std::string svalue = queryString.substr(posValue + 7,
			queryString.length() - (posValue + 7));
	//	std::cout << "\n value : " << svalue;
	std::string valuesString = subStrByDelim(svalue, '(', ')');
	std::vector < std::string > values = split(valuesString, ',');

	Row *row = new Row();
	for (int i = 0; i < (int) colNames.size(); i++) {
		row->addData(colNames[i], values[i]);
	}
	table->addRow(row);
	std::cout << "\n Inserted data."  << std::endl;
}

// DROP TABLE games;
void Query::executeDropQuery() {
	bool found = false;
	std::string tableName = queryString.substr(queryString.find("table")+6, (queryString.find(';') - (queryString.find("table") + 5) - 1));
	for (std::list<Table*>::iterator it = tables.begin(); it != tables.end(); it++) {
		Table *p = *it;

		std::cout << "\n table name : " << tableName << std::endl	;
		if (p->getName() == tableName) {
			tables.erase(it);
			found = true;
			std::cout << "\n Deleted table 	" << tableName << std::endl;
			break;
		}
	}
	if (!found) {
		std::cout << "\n Table "<< tableName <<" does not exist ! \n 	" << std::endl;
	}
}

// DELETE FROM games WHERE yr=2000;
void Query::executeDeleteQuery() {

	// check if table exist
	Table *table = getTableByName(vQuery[2]);
	if (table == NULL) {
		std::cout << "\n Table does not exist ! \n 	" << std::endl;
		return;
	}

	// parse the param
	std::string param = queryString.substr(queryString.find("where") + 6,
			(queryString.find(';') - (queryString.find("where") + 5)) - 1);

	char con;
	std::vector < std::string > vParam;

	if ((int) param.find('=') > 0) {
		vParam = split(param, '=');
		con = '=';
	} else if ((int) param.find('>') > 0) {
		vParam = split(param, '>');
		con = '>';
	} else {
		std::cout << "\n This condition is not supported yet ! \n 	"
				<< std::endl;
		return;
	}

	if (vParam.size() != 2) {
		std::cout << "\n Missing parameter for where clause ! \n 	"
				<< std::endl;
		return;
	}

	// check column
	Column *col = table->getColumnByName(vParam[0]);
	if (col == NULL) {
		std::cout << "\n Column does not exist ! \n 	" << std::endl;
		return;
	}

	table->deleteRow(col->getName(), vParam[1], con);

}

// SELECT name, region, population FROM bbc where name=viet;
void Query::executeSelectQuery() {
	// get table name
	std::string tableName;
	if( ( int)queryString.find("where")  > 0 ){
		tableName = queryString.substr(queryString.find("from")+5, (queryString.find("where") - (queryString.find("from") + 4) - 1));
	}else {
		tableName = queryString.substr(queryString.find("from")+5, (queryString.find(';') - (queryString.find("from") + 4) - 1));
	}
	tableName = trim(tableName);
	Table *table = getTableByName(tableName);
	if (table == NULL) {
		std::cout << "\n Table does not exist ! \n 	" << std::endl;
		return;
	}

	// check column
	std::list<Column*> cols = table->getAllCols();
	std::vector <std::string> vCol ;
	std::string sCol = queryString.substr(queryString.find("select")+7, ( queryString.find("from") - (queryString.find("select")+7)-1));

	// parse column
	if( (int)sCol.find('*') >-1 ){
		// show all column
		for (std::list<Column*>::iterator it = cols.begin(); it != cols.end(); it++){
				Column *p = *it;
				vCol.push_back( p->getName() );
		}
	}else {
		vCol = split(sCol, ',');
		// check if all colums entered  exist
		bool colexist = false;
		for (int i = 0; i < (int) vCol.size(); i++) {
			colexist = false;
			for (std::list<Column*>::iterator it = cols.begin(); it != cols.end(); it++) {
				Column *col = *it;
				vCol[i] = trim(vCol[i]);
				if (vCol[i] == col->getName()) {
					colexist = true;
				}
			}
			if (colexist == false) {
				std::cout << "\n Column " << vCol[i] << " does not exist !"
						<< std::endl;
				return;
			}
		}
	}

	// check where condition
	if( ( int)queryString.find("where")  > 0 ){
		std::string param = queryString.substr(queryString.find("where") + 6,
				(queryString.find(';') - (queryString.find("where") + 5)) - 1);

		char con;
		std::vector < std::string > vParam;

		if ((int) param.find('=') > 0) {
			vParam = split(param, '=');
			con = '=';
		} else if ((int) param.find('>') > 0) {
			vParam = split(param, '>');
			con = '>';
		} else {
			std::cout << "\n This condition is not supported yet ! \n 	"
					<< std::endl;
			return;
		}

		if (vParam.size() != 2) {
			std::cout << "\n Missing parameter for where clause ! \n 	"
					<< std::endl;
			return;
		}

		// check column in where clause
		Column *col = table->getColumnByName(vParam[0]);
		if (col == NULL) {
			std::cout << "\n Column does not exist ! \n 	" << std::endl;
			return;
		}

		std::list<Row*> listRow = table->listRows(col->getName(), vParam[1], con);
		if( listRow.empty() ) {
			std::cout << "\n Could not find row! \n 	" << std::endl;
			return;
		}

		// print rows

		table->printRows(listRow,vCol);

	}else {
		// there is no where condition , list all
		table->printAllRowsByColumn(vCol);
	}
}

//TODO LOAD DATA INFILE 'ls.dat' INTO TABLE t1
void Query::load(){
	std::string fileName = vQuery[3];
	std::ifstream in(fileName.data());
	if(!in){
		std::cout << "Cannot open file."  << std::endl;
		return;
	}
	char str[1000];
	std::string colName = "";
	std::string dataType = "";
	int dataLength = 0;
	bool isNull = false;
	bool isPK = false;
	while(in){
		in.getline(str, 1000);
		std::string line(str);
		if( line.size()==0 ) break;
		std::vector<std::string> v;
		v = parseQuery(line);

		std::string tableName = v[0];
		std::string cols = v[1];
		std::string data = v[2];
		tableName = trim(tableName);

		// create table
		Table *table = new Table(tableName);


		// add column to table
		std::vector<std::string> vCol = split(cols,',');
		std::vector<std::string> vColNames;
		for( int i=0; i< (int)vCol.size(); i++ ){
			colName = "";
			dataType = "";
			dataLength = 0;
			isNull = false;
			isPK = false;
			std::vector<std::string> vp ;

			vp = split(vCol[i],'|');
			// name | datatype | data length | pk | null
			colName = vp[0];
			vColNames.push_back(colName);
			dataType = vp[1];

			std::istringstream sLength(v[2]);
			sLength >> dataLength;

			std::istringstream sPk(vp[3]);
			sPk >> isPK;

			std::istringstream sNull(vp[4]);
			sNull >> isNull;

			Column *col = new Column(colName, dataType, dataLength, isNull, isPK);
			table->addColumn(col);
		}


		//  parse comma
		std::vector<std::string> vData = split(data,',');
		for( int i=0; i< (int)vData.size(); i++ ){
			// parse "|"
			std::vector<std::string> vp = split(vData[i],'|');

			Row *row = new Row();

			for( int j=0; j<(int)vColNames.size(); j++ ){
				row->addData(vColNames[j], vp[j]);
			}
			table->addRow(row);
		}
		tables.push_back(table);
	}
	in.close();

	std::cout << "\n Loaded file."  << std::endl;

}
//TODO UNLOAD TO 'cust_file' DELIMITER ','
//games id|int|11|1|0,col:name|varchar|20|0|1 1|viet,2|ti,3|loan
//student id|int|11|1|0,name|varchar|20|0|1 1|viet,2|ti,3|loan
void Query::unload(){
	std::string fileName = vQuery[2];
	std::string sDelim = vQuery[4];
	char delim = sDelim[1];

	std::string line ="";
	// loop through all tables
	for (std::list<Table*>::iterator itTable = tables.begin(); itTable != tables.end(); itTable++) {
		Table *table = *itTable;

		// table name
		line+=table->getName();

		line+=" ";
		std::list<Column*> cols = table->getAllCols();
		std::stringstream oss;

		// loop through each column
		for (std::list<Column*>::iterator itCol = cols.begin(); itCol != cols.end(); itCol++){
			Column *col = *itCol;
			line+=col->getName();
			line+="|";
			line+=col->getDataType();
			line+="|";
			oss << col->getDataLength();
			line += oss.str() ; oss.clear(); oss.str("");
			line+="|";
			oss << col->getIsPK();
			line += oss.str();  oss.clear(); oss.str("");
			line+="|";
			oss << col->getIsNull();
			line += oss.str();  oss.clear(); oss.str("");
			if ( std::distance(itCol,cols.end()) != 1)
			{
				line+=delim;
			}
		}
		line+=" ";

		// add data value

		std::list<Row*> rows = table->getAllRows();
		for (std::list<Row*>::iterator itRow = rows.begin(); itRow != rows.end(); itRow++){
			Row *row = *itRow;
			for (std::list<Column*>::iterator itCol = cols.begin(); itCol != cols.end(); itCol++){
				Column *col = *itCol;
				line+=row->getData(col->getName());
				if ( std::distance(itCol,cols.end()) != 1)
				{
					line+="|";
				}
			}
			if ( std::distance(itRow,rows.end()) != 1)
			{
				line+=delim;
			}
		}
		if ( std::distance(itTable,tables.end()) != 1)
		{
			line += "\n";
		}

	}
	std::cout << "\n line " << line << std::endl;
	std::ofstream file (fileName.data());
	file << line;
	file.close();

	std::cout << "\n Unloaded  file."  << std::endl;
}

Table* Query::getTableByName(const std::string &name) {
	for (std::list<Table*>::iterator it = tables.begin(); it != tables.end(); it++) {
		Table *p = *it;
		if (p->getName() == name) {
			return p;
		}
	}
	return NULL;
}
